/* @flow */

import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
import { observe } from 'core/observer/index'

import {
  warn,
  extend,
  nextTick,
  mergeOptions,
  defineReactive
} from '../util/index'

/**
 *  执行时机最早,vue全局初始化的时候就开始执行了
 */
export function initGlobalAPI (Vue: GlobalAPI) {
  // config
  const configDef = {}
  configDef.get = () => config
  if (process.env.NODE_ENV !== 'production') {
    configDef.set = () => {
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
  }

  //这里提供了一种防修改的方式,获取Vue.config没有问题,但是修改就爆出警告.config以闭包的形式向外暴露
  Object.defineProperty(Vue, 'config', configDef)

  // exposed util methods.
  // NOTE: these are not considered part of the public API - avoid relying on
  // them unless you are aware of the risk.

  /**
   *   mergeOptions首先将child的数据进行规范化,随后将parent和child进行合并.
   *   parent和child存在相同的key,用child.否则用parent
   * 
   */
  Vue.util = {
    warn,
    extend, 
    mergeOptions, 
    defineReactive
  }
  
  /**
   *   $set底层如果是数组,它直接使用splice切割
   *   
   *   如果是对象,发现key有没有,有直接返回
   *    
   *   没有重新添加上,它会判端原有的对象是不是响应式,如果是就给新添加的属性定义defineReactive,如果不是单纯赋值
   */
  Vue.set = set


  /***   
   *     数组使用splice
   *     
   *     对象使用
   *     delete target[key];
   *     ob.dep.notify()
   */
  Vue.delete = del
  Vue.nextTick = nextTick

  // 2.6 explicit observable API
  Vue.observable = <>(obj: T): T => {
    observe(obj)
    return obj
  }

  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })

  // this is used to identify the "base" constructor to extend all plain-object
  // components with in Weex's multi-instance scenarios.
  Vue.options._base = Vue

  extend(Vue.options.components, builtInComponents)
  
  /**
       Vue.use = function (plugin: Function | Object){
           const installedPlugins =  (this.installedPlugins || installedPlugins = []);
           ...
           plugin.plugin.call(this,this,...arguments);
       }
   */
  initUse(Vue)
  
  initMixin(Vue)
  initExtend(Vue)
  initAssetRegisters(Vue)
}
